use crate::action::record::{
    get_daily_stat, get_month_stat, get_week_stat, get_year_stat,
};
use crate::data::message::Message;
use crate::model::date::{Day, Month, Week, Year};
use crate::{fairing::record::RecordDb, model::user::UserData};
use rocket::http::Status;
use rocket::request::Request;
use rocket::response::{self, Responder, Response};
use rocket::route::Route;
use rocket::serde::{json::Json, Serialize};
use rocket_db_pools::{
    sqlx::{self, Acquire},
    Connection,
};

#[derive(Serialize)]
struct StatEntry<T: Serialize> {
    time: T,
    count: u32,
}

enum StatResult<T: Serialize> {
    Ok(Vec<StatEntry<T>>),
    Err(sqlx::Error),
}

impl<'r, T: Serialize> Responder<'r, 'static> for StatResult<T> {
    fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> {
        match self {
            StatResult::Ok(entries) => Response::build_from(Json(entries).respond_to(req)?)
                .status(Status::Ok)
                .ok(),
            StatResult::Err(err) => Response::build_from(
                Json(Message {
                    message: err.to_string(),
                })
                .respond_to(req)?,
            )
            .status(Status::InternalServerError)
            .ok(),
        }
    }
}

impl From<sqlx::Error> for StatResult<Day> {
    fn from(err: sqlx::Error) -> Self {
        StatResult::Err(err)
    }
}

#[get("/stat?<start>&<end>&type=yearly", rank = 4)]
async fn list_yearly(
    mut db: Connection<RecordDb>,
    _user: UserData,
    start: Year,
    end: Year,
) -> StatResult<Year> {
    let result = async {
        let mut tx = db.begin().await?;
        Ok(get_year_stat(&mut tx, start, end)
            .await?
            .into_iter()
            .map(|item| StatEntry {
                time: item.0,
                count: item.1,
            })
            .collect())
    }
    .await;
    match result {
        Ok(res) => StatResult::Ok(res),
        Err(err) => StatResult::Err(err),
    }
}

#[get("/stat?<start>&<end>&type=monthly", rank = 3)]
async fn list_monthly(
    mut db: Connection<RecordDb>,
    _user: UserData,
    start: Month,
    end: Month,
) -> StatResult<Month> {
    let result = async {
        let mut tx = db.begin().await?;
        Ok(get_month_stat(&mut tx, start, end)
            .await?
            .into_iter()
            .map(|item| StatEntry {
                time: item.0,
                count: item.1,
            })
            .collect())
    }
    .await;
    match result {
        Ok(res) => StatResult::Ok(res),
        Err(err) => StatResult::Err(err),
    }
}

#[get("/stat?<start>&<end>&type=weekly", rank = 2)]
async fn list_weekly(
    mut db: Connection<RecordDb>,
    _user: UserData,
    start: Week,
    end: Week,
) -> StatResult<Week> {
    let result = async {
        let mut tx = db.begin().await?;
        Ok(get_week_stat(&mut tx, start, end)
            .await?
            .into_iter()
            .map(|item| StatEntry {
                time: item.0,
                count: item.1,
            })
            .collect())
    }
    .await;
    match result {
        Ok(res) => StatResult::Ok(res),
        Err(err) => StatResult::Err(err),
    }
}

#[get("/stat?<start>&<end>&type=daily", rank = 1)]
async fn list_daily(
    mut db: Connection<RecordDb>,
    _user: UserData,
    start: Day,
    end: Day,
) -> StatResult<Day> {
    let result = async {
        let mut tx = db.begin().await?;
        Ok(get_daily_stat(&mut tx, start, end)
            .await?
            .into_iter()
            .map(|item| StatEntry {
                time: item.0,
                count: item.1,
            })
            .collect())
    }
    .await;
    match result {
        Ok(res) => StatResult::Ok(res),
        Err(err) => StatResult::Err(err),
    }
}

pub fn route() -> Vec<Route> {
    routes![list_yearly, list_monthly, list_weekly, list_daily]
}
